﻿using Grow.Domain.EventBus;
using Grow.Json;
using Grow.Module.Service;
using Grow.Utils.Exceptions;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Grow.EventBus.Cap.PublisherDomainEvents
{
    public class DomainEventPublisher : IDomainEventPublisher
    {
        private readonly List<PublisherDomainEventDto> _publisherDomainEventDtos;
        private readonly IServiceContext _serviceContext;
        private readonly DomainEventBusOptions _eventBusOptions;
        private readonly IJsonContext _jsonContext;
        private readonly IPublisherDomainEventRepository _publisherDomainEventRepository;
        private readonly IPublisherDomainEventHandler _publisherHandler;
        public DomainEventPublisher(IServiceContext serviceContext)
        {
            _publisherDomainEventDtos = new();
            _serviceContext = serviceContext;
            _eventBusOptions = _serviceContext.GetOptions<DomainEventBusOptions>();
            _jsonContext = _serviceContext.GetRequiredService<IJsonContext>();
            _publisherDomainEventRepository = _serviceContext.GetRequiredService<IPublisherDomainEventRepository>();
            _publisherHandler = _serviceContext.GetRequiredService<IPublisherDomainEventHandler>();
        }



        public async Task SaveAsync(IDomainEvent domainEvent, CancellationToken cancellationToken = default)
        {
            var sendAt = DateTimeOffset.Now;
            var eventDataOptions = _serviceContext.GetOptions<DomainEventOptions>(typeof(IDomainEvent).FullName);

            var eventDataJson = await _jsonContext.SerializeAsync(domainEvent, cancellationToken);
            if (eventDataJson is null)
            {
                throw new GrowCheckException($"eventDataId  {domainEvent.Id} eventDataJson is null");
            }

            PublisherDomainEvent publisherDomainEvent = new(
                domainEvent.Id,
                _eventBusOptions.EnvironmentName,
                eventDataOptions.EventName,
                eventDataJson,
                PublisherDomainEventStatus.Wait,
                0,
                sendAt,
                null,
                null);

            await _publisherDomainEventRepository.AddAsync(publisherDomainEvent, cancellationToken);

            _publisherDomainEventDtos.Add(new PublisherDomainEventDto(
                domainEvent.Id,
                eventDataOptions.EventName,
                eventDataJson,
                PublisherDomainEventStatus.Wait,
                0,
                sendAt,
                null,
                null)
                );
        }

        public Task PublisherAsync(CancellationToken cancellationToken = default)
        {
            _ = Task.Factory.StartNew(async () =>
            {
                foreach (var publisherDomainEventDto in _publisherDomainEventDtos)
                {
                    await _publisherHandler.PublisherAsync(publisherDomainEventDto, cancellationToken);
                }
                _publisherDomainEventDtos.Clear();
            }, cancellationToken, TaskCreationOptions.LongRunning, TaskScheduler.Default);
            return Task.CompletedTask;
        }
    }
}
